namespace Microsoft.ServiceModel.Samples.Discovery.Protocol
{
    using System;
    using System.Xml;
    using System.Xml.Serialization;
    using System.ServiceModel;
    using System.ServiceModel.Channels;
    using System.Diagnostics;
  
    [ServiceBehavior(
        InstanceContextMode = InstanceContextMode.Single,
        ConcurrencyMode=ConcurrencyMode.Multiple)]
    public sealed class FindListener : IFindContract, IDisposable
    {
        InputBinding inputBinding;
        ServiceHost serviceHost;
        bool disposed;
        IFindRequestHandler findRequestHandler;
        
        public FindListener(IFindRequestHandler findRequestHandler)
            : this(findRequestHandler, ProtocolUtility.CreateMulticastDuplexInputBinding())
        {
        }

        public FindListener(IFindRequestHandler findRequestHandler, InputBinding inputBinding)
        {
            Initialize(findRequestHandler, inputBinding);      
        }

        void Initialize(IFindRequestHandler findRequestHandler, InputBinding inputBinding)
        {
            if (inputBinding == null)
                throw new ArgumentNullException("inputBinding");

            this.inputBinding = inputBinding;

            // create the host for this announcement listener service
            this.serviceHost = new ServiceHost(this);
            
            // add the endpoint to listen on the specified "ToURI" and 
            // "ListenURI" using the specified "Binding"
            this.serviceHost.AddServiceEndpoint(typeof(IFindContract),
                this.inputBinding.Binding,
                this.inputBinding.ToUri,
                this.inputBinding.ListenUri);
                
            this.disposed = false;
            this.findRequestHandler = findRequestHandler;
            
            // open the host and start listening for find requests
            this.serviceHost.Open();
        }

        #region FindContract Members

        void IFindContract.ProbeOperation(ProbeMessage request)
        {
            if (!ValidateRequest(request)) return;

            if (this.findRequestHandler != null)
                this.findRequestHandler.HandleRequest(
                    new ProbeRequest(request.Probe, OperationContext.Current));
        }

        void IFindContract.ResolveOperation(ResolveMessage request)
        {
            if (!ValidateRequest(request)) return;

            if (this.findRequestHandler != null)
                this.findRequestHandler.HandleRequest(
                    new ResolveRequest(request.Resolve, OperationContext.Current));
        }

        static bool ValidateRequest(ProbeMessage request)
        {
            if (!ValidateHeaders()) return false;
            
            // validate the content and drop invalid message
            if (request.Probe == null)
            {
                return false;
            }

            return true;
        }

        static bool ValidateRequest(ResolveMessage request)
        {
            if (!ValidateHeaders()) return false;

            // validate the content and drop invalid message
            if ((request.Resolve == null) || (!request.Resolve.IsValid()))
            {
                return false;
            }

            return true;
        }

        static bool ValidateHeaders()
        {
            UniqueId msgId = OperationContext.Current.IncomingMessageHeaders.MessageId;
            if (msgId == null)
            {
                // drop the message because the required header MessageId is not present
                return false;
            }

            EndpointAddress replyTo = OperationContext.Current.IncomingMessageHeaders.ReplyTo;
            if (replyTo == null)
            {
                // we do not support missing ReplyTo header.
                return false;
            }

            return true;
        }

        #endregion

        #region IDisposable Members

        public void Dispose()
        {
            if (!this.disposed)
            {
                if (this.serviceHost != null)
                {
                    this.serviceHost.Close();
                }
            
                this.disposed = true;
            }
        }

        #endregion
    }
}
